//
// Created on 2024/8/1.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include "pjsip_manager.hpp"
#include "my_account.hpp"
#include "my_call.hpp"
#include "my_loger_writer.hpp"
#include <iostream>
#include <pjsua2.hpp>
#include "pjmedia-audiodev/audiodev.h"
#include <pjmedia/stream.h>
#include <chrono>
#include <thread>
#include "oh_dev.c"

#define THIS_FILE "pjsua2_manager.cpp"

using namespace pj;

bool PJSUAManager::InitSDK(bool enableUA, bool enableStun, int uniqueId_it, CallbackData *callbackData) {
    if (_sdkInstanced)
        return false;

    this->callbackData = callbackData;

    if (_uniqueId != 0)
        _uniqueId = uniqueId_it;

    try {
        _mEndpoint.libCreate();
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
        return false;
    }

    EpConfig ep_cfg;
    ep_cfg.logConfig.level = 4;
    ep_cfg.logConfig.consoleLevel = 4;

    MediaConfig media = MediaConfig();
    media.clockRate = 16000;
    media.sndClockRate = 16000;
    media.ecOptions = PJMEDIA_ECHO_WEBRTC | PJMEDIA_ECHO_USE_NOISE_SUPPRESSOR | PJMEDIA_ECHO_AGGRESSIVENESS_AGGRESSIVE;
    ep_cfg.medConfig = media;

    if (_mLogWriter != nullptr) {
        _mLogWriter = nullptr;
    }
    _mLogWriter = new MyLogWriter();
    LogConfig log_cfg;
    log_cfg.writer = _mLogWriter;
    log_cfg.decor &= ~(PJ_LOG_HAS_CR | PJ_LOG_HAS_NEWLINE);
    ep_cfg.logConfig = log_cfg;

    UaConfig ua_cfg = ep_cfg.uaConfig;
    if (enableUA) {
        ua_cfg.userAgent = "Pjsua2 OHOS SDK" + _mEndpoint.libVersion().full;
    }
    if (enableStun) {
        ua_cfg.stunServer.push_back("turn.aganyunke.com:3443");
    }
    ep_cfg.uaConfig = ua_cfg;

    try {
        _mEndpoint.libInit(ep_cfg);
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
        return false;
    }

    pjmedia_aud_subsys *aud_subsys = pjmedia_get_aud_subsys();
    for (unsigned i = 0; i < aud_subsys->drv_cnt; ++i) {
        pjmedia_aud_driver *drv = &aud_subsys->drv[i];
        int b = pjmedia_aud_unregister_factory(drv->create);
    }
    int a = pjmedia_aud_register_factory(&pjmedia_ohaudio_factory);

    int last3Digits = _uniqueId % 1000;
    if (_currentPort == 6050) {
        _currentPort = 10000 + (last3Digits * 10);
    }
    int maxport = 10000 + (last3Digits * 10) + 10;

    int port = _currentPort + 1;

    if (port >= maxport) {
        port = 10000 + (last3Digits * 10);
    }

    for (; port <= maxport; port++) {
        TransportConfig sipTpConfig;
        sipTpConfig.port = port;

        try {
            _mEndpoint.transportCreate(PJSIP_TRANSPORT_UDP, sipTpConfig);
            _currentPort = port;
            break;
        } catch (Error &e) {
            std::cout << e.reason << std::endl;
            if (port == maxport) {
                return false;
            }
        }
    }

    try {
//         CodecOpusConfig opusConfig = _mEndpoint.getCodecOpusConfig();
//         opusConfig.sample_rate = 8000;
//         opusConfig.bit_rate = 20000;
//         opusConfig.packet_loss = 20;
//         _mEndpoint.setCodecOpusConfig(opusConfig);

        if (_codecPriorities.size() == 0) {
            pj_uint8_t opus = 130;
            pj_uint8_t g722 = 129;
            pj_uint8_t pcma = 128;
            pj_uint8_t pcmu = 127;
            pj_uint8_t disable = 0;
//             _codecPriorities.insert(pair<string, pj_uint8_t>("opus/48000/2", opus));
            _codecPriorities.insert(pair<string, pj_uint8_t>("G722/16000/1", g722));
            _codecPriorities.insert(pair<string, pj_uint8_t>("PCMA/8000/1", pcma));
            _codecPriorities.insert(pair<string, pj_uint8_t>("PCMU/8000/1", pcmu));
            _codecPriorities.insert(pair<string, pj_uint8_t>("speex/32000/1", disable));
            _codecPriorities.insert(pair<string, pj_uint8_t>("speex/16000/1", disable));
            _codecPriorities.insert(pair<string, pj_uint8_t>("speex/8000/1", disable));
            _codecPriorities.insert(pair<string, pj_uint8_t>("iLBC/8000/1", disable));
            _codecPriorities.insert(pair<string, pj_uint8_t>("GSM/8000/1", disable));
        }
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
    }

    try {
        _mEndpoint.libStart();
        startUpdateResgisterChecker();
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
        return false;
    }

    if (_uniqueId == 0) {
        _uniqueId = uniqueId;
    }
    _sdkInstanced = true;
    return true;
}

bool PJSUAManager::CreateWav(string ringPath, string hangupPath, string incomingPath) {
    // 创建 audio 准备播放
    try {
        startRingPlayer = new AudioMediaPlayer();
        startRingPlayer->createPlayer(ringPath);
        endRingPlayer = new AudioMediaPlayer();
        endRingPlayer->createPlayer(hangupPath, PJMEDIA_FILE_NO_LOOP);
        incomingRingPlayer = new AudioMediaPlayer();
        incomingRingPlayer->createPlayer(incomingPath);
        startChecker();
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
    }
    return true;
}

bool PJSUAManager::DeInitSDK() {
    try {
        _currentCall = nullptr;
        _password = "";
        _username = "";
        _ipAddress = "";
        _sdkInstanced = false;
        delete _med_port;
        delete _remote_med_port;
        _med_port = nullptr;
        _remote_med_port = nullptr;
        delete startRingPlayer;
        startRingPlayer = nullptr;
        delete endRingPlayer;
        endRingPlayer = nullptr;
        delete incomingRingPlayer;
        if (_check_wav_writer != nullptr) {
            delete _check_wav_writer;
            _check_wav_writer = nullptr;
        }
        incomingRingPlayer = nullptr;
        _mEndpoint.libDestroy();
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
        return false;
    }
    return true;
}

bool PJSUAManager::RegisterAccount(string username, string password, string ip, string port) {
    string server = ip + ":" + port;
    // 防止重复注册相同的账号密码（在 flutte hotreload 的时候)
    if (_username == username && _password == password && _ipAddress == server)
        return true;

    AccountConfig acfg;
    acfg.videoConfig.autoTransmitOutgoing = false;
    acfg.videoConfig.autoShowIncoming = false;

    acfg.idUri = "sip:" + username + "@" + server;
    acfg.regConfig.registrarUri = "sip:" + server;
    int last3Digits = _uniqueId % 1000;
    int rtpPort = 20000 + (last3Digits * 10);
    acfg.mediaConfig.transportConfig.port = rtpPort;
    acfg.mediaConfig.transportConfig.portRange = 10;

    if (!acfg.sipConfig.authCreds.empty()) {
        acfg.sipConfig.authCreds.clear();
    }
    if (password.length() > 0) {
        AuthCredInfo cred("Digest", "*", username, 0, password);
        acfg.sipConfig.authCreds.push_back(cred);
    }

    _myAccount = new MyAccount(acfg);

    try {
        _myAccount->create(acfg);
        _username = username;
        _password = password;
        _ipAddress = server;
        _isLogined = true;
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
        return false;
    }
    return true;
}

bool PJSUAManager::UnregisterAccount() {
    if (!_isLogined)
        return false;
    if (_myAccount == nullptr)
        return false;

    try {
        _myAccount->shutdown();
        _username = "";
        _password = "";
        _ipAddress = "";
        _myAccount = nullptr;
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
        return false;
    }
    return true;
}

pjsip_result PJSUAManager::MakeCall(string number, string displayNumber, string wavPath, map<string, string> xHeaders) {
    _isHangupSelf = false;
    MyCall *call = new MyCall(*_myAccount, -1);
    CallOpParam prm(true);

    std::vector<pj::SipHeader> headers = {};

    if (displayNumber.length() > 0) {
        pj::SipHeader sipHeader = {};
        sipHeader.hName = "Remote-Party-ID";
        sipHeader.hValue = "\"" + displayNumber + "\" <sip:" + displayNumber + "@" + _ipAddress + ">;party=calling";

        headers.push_back(sipHeader);
    }

    for (auto &t : xHeaders) {
        SipHeader sipHeader;
        sipHeader.hName = t.first;
        sipHeader.hValue = t.second;

        headers.push_back(sipHeader);
    }

    if (headers.size() > 0) {
        pj::SipTxOption sipTxOption = pj::SipTxOption();
        sipTxOption.headers = headers;
        prm.txOption = sipTxOption;
    }

    if (wavPath.length() > 0) {
        call->addWavpath(wavPath);
    }

    string targetUri = "sip:" + number + "@" + _ipAddress;

    try {
        _mEndpoint.audDevManager().refreshDevs();

        int device = _mEndpoint.audDevManager().getCaptureDev();
        bool isActive = _mEndpoint.audDevManager().sndIsActive();
        PJ_LOG(4, (THIS_FILE, "current device id: %d", device));
        PJ_LOG(4, (THIS_FILE, "current device isActive %d", isActive ? 1 : 0));
        AudioDevInfo info = _mEndpoint.audDevManager().getDevInfo(device);

        PJ_LOG(4, (THIS_FILE, "current device name: %s", info.name.c_str()));
        PJ_LOG(4, (THIS_FILE, "current device inputCount: %d", info.inputCount));
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
    }

    try {
        _mEndpoint.audDevManager().setCaptureDev(-1);
        _mEndpoint.audDevManager().setPlaybackDev(-1);

        PJ_LOG(4, (THIS_FILE, "current reset Dev"));
    } catch (Error &e) {
        PJ_LOG(4, (THIS_FILE, e.reason.c_str()));
        std::cout << e.reason << std::endl;
    }

    try {
        if (_myAccount == NULL) {
            throw Error();
        }
        call->makeCall(targetUri, prm);
        call->callOpParam = prm;
        _currentCall = call;

        CallInfo callInfo = call->getInfo();

        pjsip_result result;
        result.success = true;
        result.error_msg = "";
        result.call_id = callInfo.callIdString;

        return result;
    } catch (Error &e) {
        _currentCall = nullptr;
        std::cout << e.info() << std::endl;
        std::cout << e.reason << std::endl;
        pjsip_result result;
        result.success = false;
        result.error_msg = e.reason;
        result.call_id = "";

        return result;
    }
}

bool PJSUAManager::Recive() {
    if (_currentCall == nullptr)
        return false;

    try {
        CallOpParam prm;
        prm.statusCode = PJSIP_SC_OK;
        _currentCall->answer(prm);

        CallInfo info = _currentCall->getInfo();
        onCallStateChange(info);
        return true;
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
        return false;
    }
}

bool PJSUAManager::Mute() {
    if (!_sdkInstanced)
        return false;
    if (_currentCall == nullptr)
        return false;
    CallInfo info;
    try {
        info = _currentCall->getInfo();
    } catch (Error &e) {
        return false;
    }

    if (info.state != PJSIP_INV_STATE_CONFIRMED)
        return false;

    for (unsigned i = 0; i < info.media.size(); i++) {
        CallMediaInfo med_info = info.media[i];
        if ((med_info.type == PJMEDIA_TYPE_AUDIO) &&
            ((med_info.status == PJSUA_CALL_MEDIA_ACTIVE) || (med_info.status == PJSUA_CALL_MEDIA_REMOTE_HOLD))) {
            Media *m = _currentCall->getMedia(i);

            AudDevManager &aud_mgr = Endpoint::instance().audDevManager();
            AudioMedia *am = AudioMedia::typecastFromMedia(m);
            try {
                if (!_isMute) {
                    aud_mgr.getCaptureDevMedia().stopTransmit(*am);
                } else {
                    aud_mgr.getCaptureDevMedia().startTransmit(*am);
                }
            } catch (Error &e) {
                continue;
            }
        }
    }
    return true;
}

bool PJSUAManager::HangupCall() {
    if (_currentCall == nullptr) {
        return false;
    }

    string callId = "";

    try {

        CallInfo callInfo = _currentCall->getInfo();
        callId = callInfo.callIdString;

        CallOpParam prm;
        prm.statusCode = PJSIP_SC_DECLINE;
        _isHangupSelf = true;
        _currentCall->hangup(prm);
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
        _currentCall = nullptr;
        return false;
    }
    _currentCall = nullptr;
    this->sendFlutterStateChnage("DISCONNECTED", callId);
    return true;
}

void PJSUAManager::adjustVoidLevel(float level) {
    _mEndpoint.audDevManager().getCaptureDevMedia().adjustRxLevel(level);
}

void PJSUAManager::SendDTMF(string value) {
    if (_currentCall == nullptr)
        return;
    try {
        _currentCall->dialDtmf(value);
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
    }
}

void PJSUAManager::onCallStateChange(CallInfo callInfo) {
    if (_currentCall == nullptr)
        return;
    try {
        CallInfo currentCallInfo = _currentCall->getInfo();
        if (currentCallInfo.callIdString != callInfo.callIdString)
            return;

        this->sendFlutterStateChnage(callInfo.stateText, callInfo.callIdString);

        AudioMedia &play_med = _mEndpoint.audDevManager().getPlaybackDevMedia();

        if (callInfo.state == PJSIP_INV_STATE_EARLY) {
            if (_last_call_state == PJSIP_INV_STATE_INCOMING) {
                incomingRingPlayer->startTransmit(play_med);
            } else {
                startRingPlayer->startTransmit(play_med);
            }
        }

        if (callInfo.state == PJSIP_INV_STATE_CONFIRMED || callInfo.state == PJSIP_INV_STATE_DISCONNECTED) {
            startRingPlayer->stopTransmit(play_med);
            incomingRingPlayer->stopTransmit(play_med);
        }

        if (callInfo.state == PJSIP_INV_STATE_DISCONNECTED) {
            _currentCall = nullptr;

            // 结束的铃声
            if (!_isHangupSelf) {
                endRingPlayer->setPos(0);
                endRingPlayer->startTransmit(play_med);
                std::chrono::milliseconds delay(800);
                std::thread t([delay]() {
                    std::this_thread::sleep_for(delay);
                    auto callbackData = PJSUAManager::getInstace()->callbackData;
                    Message *message = new Message();
                    message->method = "hangupRing";
                    napi_call_threadsafe_function(callbackData->tsfn, message, napi_tsfn_nonblocking);
                }); 
                t.detach();
            }
        }
        _last_call_state = callInfo.state;
    } catch (Error &e) {
        std::cout << e.info() << std::endl;
        std::cout << e.reason << std::endl;
    }
}

void PJSUAManager::StopHangupRing() {
    AudioMedia &play_med = _mEndpoint.audDevManager().getPlaybackDevMedia();
    endRingPlayer->stopTransmit(play_med);
}

void PJSUAManager::onImcomingCall(int callId, string xid) {
    CallOpParam prm;
    if (_currentCall != nullptr) {
        // 如果目前有通话在进行中
        try {
            MyCall *call = new MyCall(*_myAccount, callId);
            prm.statusCode = PJSIP_SC_BUSY_HERE;
            call->hangup(prm);
            delete call;
            call = nullptr;
        } catch (Error &e) {
            std::cout << e.reason << std::endl;
            return;
        }
    } else {
        try {
            _mEndpoint.audDevManager().refreshDevs();

            int device = _mEndpoint.audDevManager().getCaptureDev();
            bool isActive = _mEndpoint.audDevManager().sndIsActive();
            PJ_LOG(4, (THIS_FILE, "current device id: %d", device));
            PJ_LOG(4, (THIS_FILE, "current device isActive %d", isActive ? 1 : 0));
            AudioDevInfo info = _mEndpoint.audDevManager().getDevInfo(device);

            PJ_LOG(4, (THIS_FILE, "current device name: %s", info.name.c_str()));
            PJ_LOG(4, (THIS_FILE, "current device inputCount: %d", info.inputCount));
        } catch (Error &e) {
            std::cout << e.reason << std::endl;
        }

        try {
            AudioDevInfoVector2 devices = _mEndpoint.audDevManager().enumDev2();

            for (size_t i = 0; i < devices.size(); i++) {
                AudioDevInfo info = devices[i];
                PJ_LOG(4, (THIS_FILE, "get device id: %d", info.id));
                PJ_LOG(4, (THIS_FILE, "get device name: %s", info.name.c_str()));
                PJ_LOG(4, (THIS_FILE, "get device drive: %s", info.driver.c_str()));
                PJ_LOG(4, (THIS_FILE, "get device inputCount: %d", info.inputCount));
                PJ_LOG(4, (THIS_FILE, "get device outputCount: %d", info.outputCount));

                if (info.inputCount > 0) {
                    _mEndpoint.audDevManager().setCaptureDev(info.id);
                    break;
                }
            }
        } catch (Error &e) {
            std::cout << e.reason << std::endl;
        }

        try {
            MyCall *call = new MyCall(*_myAccount, callId);
            CallInfo info = call->getInfo();
            prm.statusCode = PJSIP_SC_RINGING;

            call->answer(prm);
            _currentCall = call;
            sendFlutterIncomingCall(info.callIdString, xid, info.remoteUri);
        } catch (Error &e) {
            std::cout << e.reason << std::endl;
            return;
        }
    }
}

void PJSUAManager::sendFlutterStateChnage(string state_text, string callId) {
    Message *message = new Message();
    message->value1 = state_text;
    message->value2 = callId;
    message->method = "on_state_change";
    napi_call_threadsafe_function(callbackData->tsfn, message, napi_tsfn_nonblocking);
}

void PJSUAManager::sendFlutterIncomingCall(string callId, string xid, string remoteUri) {
    Message *message = new Message();
    message->value1 = xid;
    message->value2 = callId;
    message->value3 = remoteUri;
    message->method = "onIncoming";

    napi_call_threadsafe_function(callbackData->tsfn, message, napi_tsfn_nonblocking);
}

void PJSUAManager::sendFlutterRecordBuffer(string method, ByteVector buffer) {
    Message *message = new Message();
    message->value1 = method;
    message->buffer = std::make_shared<ByteVector>(buffer);
    message->method = "record";

    napi_call_threadsafe_function(callbackData->tsfn, message, napi_tsfn_nonblocking);
}

void PJSUAManager::startChecker() {
    pj_timer_entry_init(&m_check_timer, 0, NULL, &rx_check_callback);
    rx_checker_start(1000);
}

void PJSUAManager::startUpdateResgisterChecker() {
    if (!_isStartUpdaterRegister) {
        pj_timer_entry_init(&m_update_timer, 0, NULL, &register_check_callback);
        register_checker_start(56000);
        _isStartUpdaterRegister = true;
    }
}

void PJSUAManager::rx_checker_start(int msec) {

    pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();

    pj_time_val delay;
    delay.sec = msec / 1000;
    delay.msec = msec % 1000;
    pjsip_endpt_schedule_timer(endpt, &m_check_timer, &delay);
}

void PJSUAManager::rx_check_callback(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) {
    PJ_UNUSED_ARG(timer_heap);
    PJ_UNUSED_ARG(entry);

    PJSUAManager::getInstace()->watchNetwork();

    PJSUAManager::getInstace()->rx_checker_start(1000);
}

void PJSUAManager::watchNetwork() {
    if (!_sdkInstanced)
        return;
    if (_currentCall == nullptr)
        return;

    try {
        CallInfo callInfo;
        callInfo = _currentCall->getInfo();
        monitorStopAudio(callInfo);
        monitorNetworkHealth(callInfo);
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
    }
}

void PJSUAManager::monitorStopAudio(CallInfo callInfo) {
    try {
        if (callInfo.role == PJSIP_ROLE_UAS)
            return;
        if (callInfo.state != PJSIP_INV_STATE_EARLY)
            return;
        StreamStat streamState = _currentCall->getStreamStat(0);
        int pkt = streamState.rtcp.rxStat.pkt;
        if (pkt > 0) {
            AudioMedia &play_med = _mEndpoint.audDevManager().getPlaybackDevMedia();
            startRingPlayer->stopTransmit(play_med);
            incomingRingPlayer->stopTransmit(play_med);
        }
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
    }
}

void PJSUAManager::monitorNetworkHealth(CallInfo callInfo) {
    try {
        if (callInfo.state <= PJSIP_INV_STATE_EARLY || callInfo.state == PJSIP_INV_STATE_DISCONNECTED)
            return;
        if (callInfo.media.size() <= 0)
            return;
        StreamStat streamState = _currentCall->getStreamStat(0);
        int rxPkt = streamState.rtcp.rxStat.pkt;
        int rxLoss = streamState.rtcp.rxStat.loss;

        int txPkt = streamState.rtcp.txStat.pkt;
        int txLoss = streamState.rtcp.txStat.loss;
        if (rxPkt - lastRxPkt < 0) {
            lastRxLoss = rxLoss;
            lastRxPkt = rxPkt;
            badTimes = 0;
            goodTimes = 0;
            badPktTimes = 0;
            currentNetworkStatus = "";
            return;
        }

        long diffLoss = rxLoss - lastRxLoss;
        long diffPkt = rxPkt - lastRxPkt;

        lastRxLoss = rxLoss;
        lastRxPkt = rxPkt;

        // 如果多次数据差值等于0， 则证明没网
        if (diffPkt == 0) {
            badPktTimes += 1;
            if (badTimes == 3) {
                _currentCall->reinvite(_currentCall->callOpParam);
            }
            if (badPktTimes > 3) {
                updateNetQuality("NONE", 0);
            }
            return;
        } else {
            if (badPktTimes > 3) {
                updateNetQuality("GOOD", 0);
            }
            badPktTimes = 0;
        }

        // 如果多次丢包比例大于 5% ，则证明网络不行
        long loss = diffLoss * 100 / (diffPkt + diffLoss);
        PJ_LOG(4, (THIS_FILE, "network monitor :: txLoss:%d, txPkt:%d, rxLoss:%d, rxPkt:%d", txLoss, txPkt, rxLoss,
                   rxPkt));

        if (loss > 5) {
            goodTimes = 0;
            badTimes += 1;
            if (badTimes > 3) {
                updateNetQuality("BAD", 0);
            }
            return;
        } else {
            goodTimes += 1;
            if (goodTimes > 5) {
                badTimes = 0;
                updateNetQuality("GOOD", 0);
            }
        }
    } catch (Error &e) {
        std::cout << e.reason << std::endl;
    }
}

void PJSUAManager::updateNetQuality(string quality, int loss) {
    Message *message = new Message();
    message->value1 = quality;
    message->intValue1 = loss;
    message->method = "quality";

    napi_call_threadsafe_function(callbackData->tsfn, message, napi_tsfn_nonblocking);
}

void PJSUAManager::register_checker_start(int msec) {

    pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();

    pj_time_val delay;
    delay.sec = msec / 1000;
    delay.msec = msec % 1000;
    pjsip_endpt_schedule_timer(endpt, &m_update_timer, &delay);
}

void PJSUAManager::updateRegister() {
    if (_myAccount != nullptr) {
        try {
            _myAccount->setRegistration(true);
        } catch (Error &e) {
            std::cout << e.info() << std::endl;
        }
    }
}

void PJSUAManager::register_check_callback(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) {
    PJ_UNUSED_ARG(timer_heap);
    PJ_UNUSED_ARG(entry);

    PJSUAManager::getInstace()->updateRegister();

    PJSUAManager::getInstace()->register_checker_start(56000);
}

string PJSUAManager::current_working_directory() { return ""; }

void PJSUAManager::StopPlayer() {
    if (_currentCall == nullptr) {
        return;
    }
    _currentCall->stopPlayer();
}

void PJSUAManager::StartRecord() {
    if (_med_port != nullptr) {
        return;
    }
    _med_port = new MyAudioMediaPort("method_send_record_bytes");

    MediaFormatAudio fmt = {};
    fmt.type = PJMEDIA_TYPE_AUDIO;
    fmt.clockRate = 16000;
    fmt.channelCount = 1;
    fmt.bitsPerSample = 16;
    fmt.frameTimeUsec = 40000;

    _med_port->createPort("med_port", fmt);

    _mEndpoint.audDevManager().getCaptureDevMedia().startTransmit(*_med_port);
}

void PJSUAManager::StopRecord() {
    if (_med_port == nullptr) {
        return;
    }
    _mEndpoint.audDevManager().getCaptureDevMedia().stopTransmit(*_med_port);

    delete _med_port;
    _med_port = nullptr;
}

bool PJSUAManager::StartRecordRemote() {
    if (_remote_med_port != nullptr) {
        return false;
    }
    if (_currentCall == nullptr || _currentCall->current_am == nullptr) {
        return false;
    }
    _remote_med_port = new MyAudioMediaPort("method_send_remote_record_bytes");

    MediaFormatAudio fmt = {};
    fmt.type = PJMEDIA_TYPE_AUDIO;
    fmt.clockRate = 16000;
    fmt.channelCount = 1;
    fmt.bitsPerSample = 16;
    fmt.frameTimeUsec = 40000;

    try {
        _remote_med_port->createPort("med_port", fmt);
        _currentCall->current_am->startTransmit(*_remote_med_port);
        _remote_med_port->bindMeida = _currentCall->current_am;
    } catch (Error &e) {
        std::cout << e.info() << std::endl;
        return false;
    }
    return true;
}

void PJSUAManager::StopRecordRemote() {
    if (_remote_med_port == nullptr) {
        return;
    }
    if (_remote_med_port->bindMeida == nullptr) {
        return;
    }

    // 已经提前销毁
    if (_remote_med_port->bindMeida->getPortId() < 0) {
        _remote_med_port->bindMeida = nullptr;
        delete _remote_med_port;
        _remote_med_port = nullptr;
        return;
    }

    try {
        _remote_med_port->bindMeida->stopTransmit(*_remote_med_port);
    } catch (Error &e) {
        std::cout << e.info() << std::endl;
        delete _remote_med_port;
        _remote_med_port = nullptr;
        return;
    }

    _remote_med_port->bindMeida = nullptr;
    delete _remote_med_port;
    _remote_med_port = nullptr;
    return;
}

void PJSUAManager::UpdateCodecWithOnlyG711(bool useOnlyG711) {
    if (!_sdkInstanced) {
        return;
    }

    try {
        CodecInfoVector2 codecInfoVector = _mEndpoint.codecEnum2();

        static string kPCMU = "PCMU/8000/1";
        static string kPCMA = "PCMA/8000/1";

        for (int i = 0; i < codecInfoVector.size(); i++) {
            CodecInfo info = codecInfoVector[i];
            string codecIdentifier = info.codecId;
            pj_uint8_t defaultPriority = 0;
            auto defaultPriority_it = _codecPriorities.find(codecIdentifier);
            if (defaultPriority_it != _codecPriorities.end()) {
                defaultPriority = defaultPriority_it->second;
            }

            if (useOnlyG711) {
                pj_uint8_t priority = 0;
                if (codecIdentifier == kPCMU || codecIdentifier == kPCMA) {
                    priority = defaultPriority;
                }
                _mEndpoint.codecSetPriority(info.codecId, priority);
            } else {
                _mEndpoint.codecSetPriority(info.codecId, defaultPriority);
            }
        }
    } catch (Error &e) {
        std::cout << e.info() << std::endl;
    }
}

bool PJSUAManager::CheckLoopAudio(string recordPath)
{
    try
    {
        AudDevManager &aud_mgr =
            Endpoint::instance().audDevManager();
        _check_wav_writer = new AudioMediaRecorder();
        _check_wav_writer->createRecorder(recordPath);
        aud_mgr.getCaptureDevMedia().startTransmit(*_check_wav_writer);
    }
    catch (Error &e)
    {
        std::cout << e.info() << std::endl;
        return false;
    }
    return true;
}

bool PJSUAManager::StopCheckLoopAudio()
{
    if (_check_wav_writer != nullptr)
    {
        try
        {
            AudDevManager &aud_mgr =
                Endpoint::instance().audDevManager();
            aud_mgr.getCaptureDevMedia().stopTransmit(*_check_wav_writer);
            delete _check_wav_writer;
            _check_wav_writer = nullptr;
        }
        catch (Error &e)
        {
            std::cout << e.info() << std::endl;
            return false;
        }
    }
    return true;
}